package com.belano.catalog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.CollectionUtils;

import com.belano.domain.Person;
import com.belano.domain.Role;
import com.sun.jersey.api.NotFoundException;

public class InMemoryPersonCatalog implements PersonCatalog, UserDetailsService {

    @SuppressWarnings("serial")
    private Map<Long, Person> catalog = new HashMap<Long, Person>() {
        {
            put(1l,
                    new Person("john", "doe", "jodoe", "tOps3cr3t", Arrays
                            .asList(new Role("ADMIN"))));
            put(2l,
                    new Person("jane", "doe", "jadoe", "tOps3cr3t", Arrays
                            .asList(new Role("USER"))));
        }
    };

    @Override
    public Person getPerson(Long personId) {        
        if (!catalog.containsKey(personId)) {
            throw new CatalogException("personId '" + personId
                    + "' not found...");
        }
        return catalog.get(personId);
    }

    @Override
    public void savePerson(Person person) {
        System.out.println("Person " + person);
        catalog.put(person.getId(), person);
    }

    @Override
    public List<Person> getPeople() {
        return new ArrayList<Person>(catalog.values());
    }

    @Override
    public UserDetails loadUserByUsername(final String username)
            throws UsernameNotFoundException, DataAccessException {
        Person found = getFirst(filterCatalog(new Predicate() {
            @Override
            public boolean apply(Person person) {
                return person.getUsername().equals(username);
            }
        }));

        if (found == null) {
            throw new UsernameNotFoundException("user '" + username
                    + "' not found...");
        }

        return found;
    }
    
    @Override
    public Person authenticatePerson(final String username,
            final String password) throws AuthenticationException {
        List<Person> validUsers = filterCatalog(new Predicate() {
            @Override
            public boolean apply(Person person) {
                return (person.getUsername().equals(username) && person
                        .getPassword().equals(password));
            }
        });

        if (validUsers.isEmpty())
            throw new AuthenticationException(String.format(
                    "Could not authenticate %s", username));

        return getFirst(validUsers);
    }

    private static <T> T getFirst(List<T> list) {
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    private List<Person> filterCatalog(Predicate predicate) {
        List<Person> validUsers = new ArrayList<Person>();
        for (Map.Entry<Long, Person> entry : catalog.entrySet()) {
            Person person = entry.getValue();
            if (predicate.apply(person)) {
                validUsers.add(person);
            }
        }
        return validUsers;
    }

    interface Predicate {
        boolean apply(Person person);
    }

}
